#!/usr/bin/env python3
"""
TaoBao Redis任务监听器
监听Redis队列中的TaoBao爬取任务并执行
"""

import redis
import json
import time
import logging
import pickle
from typing import Dict, List, Optional, Any
import sys
import os
from selenium.webdriver.common.by import By

# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from TaoBao.scraper import TaoBaoScraper
import requests

# Redis连接配置
REDIS_CONFIG = {
    'host': '8.155.9.53',
    'port': 6300,
    'password': 'jhkdjhkjdhsIUTYURTU_XF6dAk',
    'task_key': ':1:自动获取产品列表'
}

# API提交配置
API_CONFIG = {
    'url': 'http://192.168.5.12:8000/api/v1/products/batch_add_processed_products/',
    'headers': {
        'Content-Type': 'application/json',
        'User-Agent': 'TaoBao-Scraper/1.0'
    },
    'timeout': 30
}

class TaoBaoRedisMonitor:
    """TaoBao Redis任务监听器"""
    
    def __init__(self):
        """初始化监听器"""
        self.redis_client = None
        self.logger = self._setup_logging()
        self._connect_redis()
        
        # 添加统计信息
        self.stats = {
            'total_tasks': 0,
            'successful_tasks': 0,
            'failed_tasks': 0,
            'start_time': None
        }
    
    def _setup_logging(self) -> logging.Logger:
        """设置日志记录器"""
        logger = logging.getLogger('TaoBaoRedisMonitor')
        logger.setLevel(logging.INFO)
        
        # 创建控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        
        # 创建格式器
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        console_handler.setFormatter(formatter)
        
        # 添加处理器到logger
        if not logger.handlers:
            logger.addHandler(console_handler)
        
        return logger
    
    def _connect_redis(self) -> bool:
        """连接Redis服务器"""
        try:
            # 提取Redis连接参数 - 使用与JD完全一致的方式
            redis_params = {
                'host': REDIS_CONFIG['host'],
                'port': REDIS_CONFIG['port'],
                'password': REDIS_CONFIG['password'],
                'decode_responses': False  # 与JD保持一致，直接设置
            }
            
            self.redis_client = redis.Redis(**redis_params)
            # 测试连接
            self.redis_client.ping()
            self.logger.info(f"✅ 成功连接到Redis服务器: {REDIS_CONFIG['host']}:{REDIS_CONFIG['port']}")
            return True
        except redis.ConnectionError as e:
            self.logger.error(f"❌ Redis连接被拒绝: {e}")
            self.logger.error("💡 请检查：")
            self.logger.error(f"   1. Redis服务器 {REDIS_CONFIG['host']}:{REDIS_CONFIG['port']} 是否可访问？")
            self.logger.error("   2. 网络连接是否正常？")
            self.logger.error("   3. Redis密码是否正确？")
            self.redis_client = None
            return False
        except redis.AuthenticationError as e:
            self.logger.error(f"❌ Redis认证失败: {e}")
            self.logger.error("💡 请检查Redis密码是否正确")
            self.redis_client = None
            return False
        except Exception as e:
            self.logger.error(f"❌ 连接Redis失败: {e}")
            self.redis_client = None
            return False
    
    def get_redis_tasks(self):
        """获取Redis中的任务列表 - 完全按照JD方式"""
        try:
            raw_data = self.redis_client.get(REDIS_CONFIG['task_key'])
            
            if not raw_data:
                return []
            
            # 智能解析数据（兼容pickle和JSON，与JD完全一致）
            try:
                import pickle
                data = pickle.loads(raw_data)
                if isinstance(data, str):
                    return json.loads(data)
                elif isinstance(data, list):
                    return data
                else:
                    return []
            except:
                try:
                    text_data = raw_data.decode('utf-8')
                    return json.loads(text_data)
                except:
                    self.logger.error("❌ 无法解析Redis数据格式")
                    return []
                    
        except Exception as e:
            self.logger.error(f"❌ 获取Redis任务失败: {e}")
            return []
    
    def update_redis_tasks(self, task_list):
        """更新Redis中的任务列表 - 完全按照JD方式"""
        try:
            if task_list:
                json_data = json.dumps(task_list, ensure_ascii=False)
                self.redis_client.set(REDIS_CONFIG['task_key'], json_data.encode('utf-8'))
                self.logger.info(f"💾 已更新Redis，剩余任务: {len(task_list)}")
            else:
                self.redis_client.delete(REDIS_CONFIG['task_key'])
                self.logger.info("🗑️ 任务队列已清空")
        except Exception as e:
            self.logger.error(f"❌ 更新Redis失败: {e}")
    
    def _validate_task_data(self, task: Dict) -> tuple[bool, str]:
        """验证任务数据格式"""
        # 检查必需字段
        required_fields = ['platform', 'store_id', 'store_url']
        
        for field in required_fields:
            if field not in task:
                return False, f"缺少必需字段: {field}"
            if not task[field]:
                return False, f"字段 {field} 不能为空"
        
        # 验证平台
        if task['platform'] != '淘宝':
            return False, f"平台必须是'淘宝'，当前为: {task['platform']}"
        
        # 验证店铺URL格式
        store_url = task['store_url']
        if not self._validate_taobao_url(store_url):
            return False, f"淘宝店铺URL格式无效: {store_url}"
        
        return True, "验证通过"
    
    def _validate_taobao_url(self, url: str) -> bool:
        """验证淘宝店铺URL格式"""
        valid_patterns = [
            'shop',
            'taobao.com',
        ]
        
        return any(pattern in url.lower() for pattern in valid_patterns)
    
    def get_taobao_login_mode(self):
        """获取TaoBao登录方式"""
        print("\n🔐 请选择TaoBao登录方式:")
        print("  1. 交互式登录（终端输入用户名密码，程序自动填写）")
        print("  2. 手动登录（浏览器中完全手动操作，适合有验证码的情况）")
        
        while True:
            choice = input("请选择 (1/2, 默认1): ").strip()
            if choice in ['', '1']:
                return "interactive"
            elif choice == '2':
                return "manual"
            else:
                print("❌ 无效选择，请输入 1 或 2")

    def manual_taobao_login_process(self, scraper):
        """TaoBao手动登录流程"""
        try:
            self.logger.info("🌐 正在打开TaoBao登录页面...")
            # 导航到TaoBao登录页面
            scraper.driver.get("https://login.taobao.com/")
            
            print("\n" + "="*60)
            print("🔐 TaoBao手动登录模式")
            print("="*60)
            print("📋 请在浏览器中完成以下操作：")
            print("   1. 选择登录方式（账号密码/短信/扫码等）")
            print("   2. 输入用户名和密码")
            print("   3. 完成验证码验证（如果有）")
            print("   4. 勾选用户协议（如果需要）")
            print("   5. 点击登录按钮")
            print("   6. 等待登录成功")
            print("   7. 登录成功后回到此窗口按 Enter 键继续")
            print("-" * 60)
            
            # 等待用户手动登录
            input("✋ 请在浏览器中完成登录，然后按 Enter 键继续...")
            
            # 验证登录状态
            self.logger.info("🔍 验证TaoBao登录状态...")
            
            # 检查当前URL是否表示登录成功
            current_url = scraper.driver.current_url
            if "login" not in current_url.lower() and "taobao.com" in current_url:
                self.logger.info("✅ TaoBao登录验证成功")
                return True
            else:
                # 尝试其他方式验证登录状态
                try:
                    import time
                    time.sleep(2)
                    
                    # 检查是否有用户信息元素
                    try:
                        # 检查是否能找到登录后的用户信息
                        user_info = scraper.driver.find_element(By.CSS_SELECTOR, ".site-nav-user, .site-nav-login-info, .j_username")
                        if user_info:
                            self.logger.info("✅ 检测到用户信息，TaoBao登录成功")
                            return True
                    except:
                        pass
                    
                    # 如果还在登录页面，说明登录失败
                    if "login" in scraper.driver.current_url.lower():
                        self.logger.warning("⚠️ 仍在登录页面，可能登录失败")
                        retry = input("是否重试登录？(y/n): ").strip().lower()
                        if retry in ['y', 'yes']:
                            return self.manual_taobao_login_process(scraper)
                        return False
                    else:
                        self.logger.info("✅ 已离开登录页面，假定TaoBao登录成功")
                        return True
                        
                except Exception as e:
                    self.logger.warning(f"TaoBao登录状态验证出错: {e}")
                    # 如果验证出错，询问用户
                    success = input("TaoBao登录是否成功？(y/n): ").strip().lower()
                    return success in ['y', 'yes']
                    
        except Exception as e:
            self.logger.error(f"❌ TaoBao手动登录过程出错: {e}")
            return False
    
    def process_taobao_task(self, task: Dict, max_retries: int = 3) -> bool:
        """处理单个TaoBao任务，带重试机制"""
        # 验证任务数据
        is_valid, validation_msg = self._validate_task_data(task)
        if not is_valid:
            self.logger.error(f"❌ 任务数据验证失败: {validation_msg}")
            return False
        
        # 获取登录方式（只在第一次获取）
        login_mode = None
        
        for attempt in range(max_retries):
            try:
                retry_info = f" (尝试 {attempt + 1}/{max_retries})" if attempt > 0 else ""
                self.logger.info(f"🎯 开始处理TaoBao任务: Store ID = {task['store_id']}{retry_info}")
                
                # 只在第一次尝试时获取登录方式
                if login_mode is None:
                    login_mode = self.get_taobao_login_mode()
                    if login_mode is None:
                        self.logger.error("❌ 未选择登录方式")
                        return False
                
                # 使用TaoBaoScraper处理任务 - 使用有界面模式以便处理验证码
                with TaoBaoScraper(headless=False, debug=False) as scraper:
                    self.logger.info("🔧 TaoBao爬虫已初始化")
                    
                    login_success = False
                    if login_mode == "interactive":
                        # 交互式登录流程
                        self.logger.info("🔐 开始交互式登录流程...")
                        if not scraper.navigate_to_login_page():
                            self.logger.error("❌ 无法导航到登录页面")
                            continue  # 重试
                        
                        self.logger.info("🤖 TaoBao使用交互式登录（模拟人工输入）")
                        login_success = scraper.login_taobao()
                    elif login_mode == "manual":
                        # 手动登录流程
                        self.logger.info("👤 开始手动登录模式...")
                        login_success = self.manual_taobao_login_process(scraper)
                    
                    if not login_success:
                        self.logger.error("❌ 登录失败")
                        continue  # 重试
                    
                    # 登录成功后导航到店铺
                    self.logger.info("🏪 登录成功，导航到目标店铺...")
                    if not scraper.navigate_to_shop(task['store_url']):
                        self.logger.error("❌ 导航到店铺失败")
                        continue  # 重试
                    
                    # 获取商品列表
                    self.logger.info("📦 开始获取商品列表...")
                    products = scraper.get_product_list()
                    
                    if not products:
                        self.logger.warning("⚠️ 未找到商品")
                        continue  # 重试
                    
                    self.logger.info(f"✅ 成功获取 {len(products)} 个商品")
                    
                    # 获取商品详情
                    self.logger.info("🔍 开始获取商品详情...")
                    detailed_products = scraper.get_product_details(products)
                    
                    if not detailed_products:
                        self.logger.warning("⚠️ 未获取到详细信息")
                        continue  # 重试
                    
                    self.logger.info(f"✅ 成功获取 {len(detailed_products)} 个商品详情")
                    
                    # 转换为API格式
                    self.logger.info("🔄 转换为API格式...")
                    api_products = []
                    for product in detailed_products:
                        # 清理WebElement对象
                        clean_product = {k: v for k, v in product.items() if k != 'card_element'}
                        api_product = scraper.convert_to_api_format(clean_product)
                        if api_product:
                            api_products.append(api_product)
                    
                    if not api_products:
                        self.logger.warning("⚠️ API格式转换失败")
                        continue  # 重试
                    
                    self.logger.info(f"✅ 成功转换 {len(api_products)} 个商品为API格式")
                    
                    # 提交到API
                    if self._submit_to_api(api_products, task['store_id']):
                        self.logger.info("✅ 任务处理成功")
                        return True
                    else:
                        self.logger.error("❌ API提交失败")
                        continue  # 重试
                        
            except Exception as e:
                self.logger.error(f"❌ 处理TaoBao任务失败 (尝试 {attempt + 1}): {e}")
                if attempt < max_retries - 1:
                    self.logger.info(f"⏳ 等待 {(attempt + 1) * 5} 秒后重试...")
                    import time
                    time.sleep((attempt + 1) * 5)  # 递增等待时间
                else:
                    import traceback
                    traceback.print_exc()
        
        self.logger.error(f"❌ TaoBao任务处理失败，已尝试 {max_retries} 次")
        return False
    
    def _submit_to_api(self, products: List[Dict], store_id: str) -> bool:
        """提交商品数据到API"""
        try:
            self.logger.info(f"🚀 准备提交 {len(products)} 个商品到API...")
            
            # 准备API数据
            api_data = {
                'platform': '淘宝',
                'store_id': store_id,
                'products': products
            }
            
            # 发送POST请求
            response = requests.post(
                API_CONFIG['url'],
                json=api_data,
                headers=API_CONFIG['headers'],
                timeout=API_CONFIG['timeout']
            )
            
            if response.status_code == 200 or response.status_code == 201:
                self.logger.info(f"✅ 成功提交商品数据到API")
                return True
            else:
                self.logger.error(f"❌ API返回错误状态码: {response.status_code}")
                self.logger.error(f"响应内容: {response.text}")
                return False
                
        except requests.exceptions.RequestException as e:
            self.logger.error(f"❌ API请求失败: {e}")
            return False
        except Exception as e:
            self.logger.error(f"❌ 提交API数据时发生未知错误: {e}")
            return False
    
    def start_monitoring(self) -> None:
        """开始监听Redis任务"""
        import time
        self.stats['start_time'] = time.time()
        
        self.logger.info("🎯 TaoBao Redis任务监听器启动")
        self.logger.info(f"📡 监听Redis服务器: {REDIS_CONFIG['host']}:{REDIS_CONFIG['port']}")
        self.logger.info(f"🎯 API提交地址: {API_CONFIG['url']}")
        self.logger.info("=" * 60)
        
        while True:
            try:
                # 获取Redis中的任务
                tasks = self.get_redis_tasks()
                
                if not tasks:
                    self.logger.info("😴 暂无任务，等待5秒后继续监听...")
                    time.sleep(5)
                    continue
                
                self.logger.info(f"📋 发现 {len(tasks)} 个待处理任务")
                
                # 逐个处理任务 - 按照JD方式
                for task in tasks:
                    self.logger.info(f"🔄 处理任务: 平台={task['platform']}, Store ID={task['store_id']}")
                    
                    # 处理任务
                    self.stats['total_tasks'] += 1
                    success = self.process_taobao_task(task)
                    
                    if success:
                        self.stats['successful_tasks'] += 1
                        self.logger.info(f"✅ 任务处理成功: {task['store_id']}")
                        # 从任务列表中移除已完成的任务
                        tasks.remove(task)
                        self.update_redis_tasks(tasks)
                    else:
                        self.stats['failed_tasks'] += 1
                        self.logger.error(f"❌ 任务处理失败: {task['store_id']}")
                    
                    # 显示统计信息
                    self._show_stats()
                
                self.logger.info("🔄 继续监听新任务...")
                self.logger.info("-" * 40)
                
            except KeyboardInterrupt:
                self.logger.info("\n⏹️ 接收到中断信号，正在停止监听器...")
                break
            except Exception as e:
                self.logger.error(f"❌ 监听过程中发生错误: {e}")
                self.logger.info("⏳ 等待10秒后继续...")
                time.sleep(10)
        
        self.logger.info("👋 TaoBao Redis监听器已停止")
    
    def _show_stats(self) -> None:
        """显示统计信息"""
        if self.stats['start_time']:
            runtime = time.time() - self.stats['start_time']
            hours = int(runtime // 3600)
            minutes = int((runtime % 3600) // 60)
            
            success_rate = (self.stats['successful_tasks'] / max(self.stats['total_tasks'], 1)) * 100
            
            self.logger.info(f"📊 统计信息: 总任务={self.stats['total_tasks']}, "
                           f"成功={self.stats['successful_tasks']}, "
                           f"失败={self.stats['failed_tasks']}, "
                           f"成功率={success_rate:.1f}%, "
                           f"运行时间={hours}h{minutes}m")
    
    def test_connection(self) -> None:
        """测试连接"""
        print("🧪 TaoBao Redis监听器连接测试")
        print("=" * 50)
        
        # 测试Redis连接
        if self.redis_client:
            try:
                self.redis_client.ping()
                print("✅ Redis连接: 正常")
                
                # 测试任务key是否存在
                task_count = 0
                if self.redis_client.exists(REDIS_CONFIG['task_key']):
                    tasks = self.get_redis_tasks()
                    task_count = len(tasks)
                print(f"📊 当前任务数量: {task_count}")
                
            except redis.ConnectionError as e:
                print(f"❌ Redis连接: 被拒绝 - {e}")
                print("💡 解决方案:")
                print("   1. 启动Redis服务: redis-server")
                print("   2. 检查端口6379是否可用")
            except Exception as e:
                print(f"❌ Redis连接: 失败 - {e}")
        else:
            print("❌ Redis连接: 未建立")
            print("💡 尝试重新连接...")
            if self._connect_redis():
                print("✅ 重新连接成功")
            else:
                print("❌ 重新连接失败")
        
        # 测试API连接
        try:
            test_response = requests.get(
                API_CONFIG['url'].replace('/batch/', '/'),
                timeout=5
            )
            print(f"✅ API连接: 正常 (状态码: {test_response.status_code})")
        except Exception as e:
            print(f"❌ API连接: 失败 - {e}")
        
        # 显示配置信息
        print(f"\n📊 配置信息:")
        print(f"Redis服务器: {REDIS_CONFIG['host']}:{REDIS_CONFIG['port']}")
        print(f"Redis任务Key: {REDIS_CONFIG['task_key']}")
        print(f"API地址: {API_CONFIG['url']}")
        print(f"TaoBao登录方式: 交互式登录")
        print(f"认证状态: {'已配置密码' if REDIS_CONFIG.get('password') else '无密码'}")


def main():
    """主函数 - 完全按照JD方式"""
    print("🚀 TaoBao Redis监听器")
    print("=" * 40)
    
    monitor = TaoBaoRedisMonitor()
    
    # 测试连接
    print("🔍 测试连接...")
    monitor.test_connection()
    
    print("✅ 连接测试完成")
    
    while True:
        print("\n" + "="*40)
        print("📋 TaoBao Redis 任务管理器")
        print("="*40)
        print("1. 查看当前任务")
        print("2. 添加TaoBao测试任务")
        print("3. 添加自定义TaoBao任务")
        print("4. 清空所有任务")
        print("5. 🎧 启动监听器")
        print("6. 退出")
        print("-"*40)
        
        choice = input("请选择操作 (1-6): ").strip()
        
        if choice == "1":
            # 查看当前任务 - 完全按照JD方式
            tasks = monitor.get_redis_tasks()
            if tasks:
                print(f"\n📋 当前任务数量: {len(tasks)}")
                for i, task in enumerate(tasks, 1):
                    platform = task.get('platform', '未知')
                    store_id = task.get('store_id', '未知')
                    store_url = task.get('store_url', '未知')
                    retry_count = task.get('retry_count', 0)
                    
                    # 验证任务数据
                    is_valid = monitor._validate_task_data(task)[0] if platform == "淘宝" else "跳过验证"
                    validity_status = "✅" if is_valid is True else "❌" if is_valid is False else "⏭️"
                    
                    print(f"  {i}. 平台: {platform}, Store ID: {store_id}, 重试: {retry_count} {validity_status}")
                    print(f"     URL: {store_url}")
                    
                    if platform == "淘宝" and is_valid is False:
                        print(f"     ⚠️  任务数据验证失败，此任务可能无法正常执行")
            else:
                print("\n📭 当前无任务")
        
        elif choice == "2":
            # 添加测试任务
            print("\n➕ 添加TaoBao测试任务")
            store_url = input("请输入淘宝店铺URL (回车使用默认): ").strip()
            if not store_url:
                store_url = "https://shop185716543.taobao.com/"
            
            import uuid
            test_task = {
                'platform': '淘宝',
                'store_id': str(uuid.uuid4()),
                'store_url': store_url,
                'retry_count': 0,
                'created_at': time.strftime('%Y-%m-%d %H:%M:%S')
            }
            
            tasks = monitor.get_redis_tasks()
            tasks.append(test_task)
            monitor.update_redis_tasks(tasks)
            print("✅ 测试任务添加成功")
        
        elif choice == "3":
            # 添加自定义任务
            print("\n➕ 添加自定义TaoBao任务")
            store_url = input("请输入淘宝店铺URL: ").strip()
            if not store_url:
                print("❌ URL不能为空")
                continue
            
            store_id = input("请输入Store ID (回车自动生成): ").strip()
            if not store_id:
                import uuid
                store_id = str(uuid.uuid4())
            
            custom_task = {
                'platform': '淘宝',
                'store_id': store_id,
                'store_url': store_url,
                'retry_count': 0,
                'created_at': time.strftime('%Y-%m-%d %H:%M:%S')
            }
            
            tasks = monitor.get_redis_tasks()
            tasks.append(custom_task)
            monitor.update_redis_tasks(tasks)
            print("✅ 自定义任务添加成功")
        
        elif choice == "4":
            # 清空所有任务
            confirm = input("⚠️ 确认清空所有任务? (y/N): ").strip().lower()
            if confirm == 'y':
                monitor.update_redis_tasks([])
                print("✅ 所有任务已清空")
        
        elif choice == "5":
            # 启动监听器
            print("🚀 启动监听器...")
            print("💡 按 Ctrl+C 停止监听")
            try:
                monitor.start_monitoring()
            except KeyboardInterrupt:
                print("\n⏹️ 监听器已停止")
        
        elif choice == "6":
            print("👋 再见!")
            break
        
        else:
            print("❌ 无效选择，请重新输入")


if __name__ == "__main__":
    main() 